//! \file CConfiguration.cpp
// -----------------------------------------------------------------------------
//! This program is free software; you can redistribute it and/or
//! modify it under the terms of the GNU General Public License
//! as published by the Free Software Foundation; either version 2
//! of the License, or (at your option) any later version.
//!
//! This program is distributed in the hope that it will be useful,
//! but WITHOUT ANY WARRANTY; without even the implied warranty of
//! MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//! GNU General Public License for more details.
//!
//! You should have received a copy of the GNU General Public License
//! along with this program; if not, write to the Free Software Foundation,
//! Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// -----------------------------------------------------------------------------

// Project
#include "CConfiguration.h"
#include "System.h"
//#include "Keys.h" TODO: cleanup
//#include "CKey.h"
#include "CParameter.h"

// Standard
#include <iostream>
#include <algorithm>
#include <stdio.h>

//#define BOOST_FILESYSTEM_VERSION 3
#include <iostream>
#include <boost/filesystem.hpp>

namespace code_sheriff
{

// =====[ Static Members ]======================================================
CConfiguration* CConfiguration::m_pSingleton; //!< (Singleton) Instance.


// =====[ Public Methods ]======================================================


// =============================================================================
// CConfiguration::CConfiguration
// -----------------------------------------------------------------------------
CConfiguration::CConfiguration()
{
  m_ParameterList.clear(); // Initially our parameter list is empty.
}


// TODO: move to header
//! Returns a pointer to the instanced singleton object.
//!
//! \retval Singleton
//!   A pointer to the instanced singleton object.


// =============================================================================
// CConfiguration::getSingleton
// -----------------------------------------------------------------------------
CConfiguration* CConfiguration::getSingleton()
{
  // Only allow one instance of class to be generated.
  if (!m_pSingleton)
    { m_pSingleton = new CConfiguration; } // Create our single instance.
  return (m_pSingleton);                  // Finally, return our singleton.
}


// =============================================================================
// CConfiguration::parseCommandLine
// -----------------------------------------------------------------------------
int CConfiguration::parseCommandLine(int argc, char* argv[])
{
  std::string Line; // TODO: document

  // Validate our arguments.
  if (argc <= 1)
    { return (ERROR_NONE); } // TODO: re-evaluate this

  // Iterate through the remaining arguments.
  for (int Index = 1; Index < argc; Index++)
  {
    // Grab an argument line.
    Line = argv[Index];

    // Try the option as a "long format" option.
    if (parseLongOption(Line) == 0)
      { continue; }

    // Try the option as a "short format" option.
    if (parseShortOption(Line) == 0)
      { continue; }

    // Otherwise append the line as a value to the previous key-value.
    m_ParameterList.appendValueToLast(Line);  // TOOD: check result?
//    appendValueToLast(Line); // TOOD: check result? TODO: remove
  }

  // TODO: cleanup when done (or add argument that does this.  #debug perhaps.
  m_ParameterList.printAll();

  // Finally, return success.
  return (ERROR_NONE);
}


// =============================================================================
// CConfiguration::loadConfiguration
// -----------------------------------------------------------------------------
TERROR CConfiguration::loadConfiguration(void)
{
  CFile File;                   // The configuration file.
  std::string FileName;         // The name of the configuration file.
  boost::filesystem::path Path; // TODO: document

  // Get the key value for the configuration file.
  m_ParameterList.get(KEY_CONFIG_FILE, FileName);
  std::cout << "Using configuration file : " << FileName << std::endl; // TODO: clean later

  // Set the boost path to the requested file name.
  Path = FileName;

  // Verify that the requested file exists.
  if (boost::filesystem::exists(Path) == false)
    { return (ERROR_INVALID_FILE); }

  // Verify that the requested file is a regular file.
  if (boost::filesystem::is_regular_file(Path) == false)
    { return (ERROR_INVALID_FILE); }

  // Load the requested file.
  File.load(Path.native());

  // TODO: parse each line to configuration settings.

  return (ERROR_UNKNOWN); // TODO: implement feature.
}


// =============================================================================
// CConfiguration::getValue
// -----------------------------------------------------------------------------
TERROR CConfiguration::getValue(TKEY Key, std::string& Value, int Index)
{
  // Return the requested parameter value from the list.
  return (m_ParameterList.get(Key, Value, Index));
}


// =====[ Private Methods ]=====================================================


// =============================================================================
// CConfiguration::parseLongOption
// -----------------------------------------------------------------------------
TERROR CConfiguration::parseLongOption(std::string Option)
{
  std::string Delimiter = "--"; // TODO: document
  CParameter Parameter;         // TODO: document
  TKEY Key;                     // TODO: document
  std::string KeyText;          // TODO: document
  std::string ValueText;        // TODO: document
  size_t Pos;                   // TODO: document

  // Validate our arguments.
  Pos = Option.find(Delimiter);
  if (Pos == std::string::npos)
    { return (ERROR_INVALID_ARGUMENT); }

  // Remove the delimiter.
  KeyText = Option = Option.replace(0, Delimiter.length(), "");

  // KeyText can be terminated by a '='.
  if ((Pos = KeyText.find('=')) != std::string::npos)
    { KeyText = KeyText.substr(0, Pos); }

  // Value is everything that is left.
  ValueText = Option.replace(0, KeyText.length(), "");
  if (ValueText.length() > 0)
    ValueText = ValueText.replace(0, 1, ""); // Strip the value delimiter.

  // Append the key-value to the list.
  Parameter.clear();                  // Clear the object.
  Key = CKey::toKey(KeyText);         // Convert the text to the key.
  Parameter.setKey(Key);              // Set the key name.
  Parameter.appendValue(ValueText);   // Set the value.
  m_ParameterList.append(Parameter);  // Add the key-value to our list.

  // Finally, return success.
  return (ERROR_NONE);
}


// =============================================================================
// CConfiguration::parseShortOption
// -----------------------------------------------------------------------------
TERROR CConfiguration::parseShortOption(std::string Option)
{
  CParameter Parameter;   // TODO: document
  TKEY Key;               // TODO: document
  std::string KeyText;    // TODO: document
  size_t Index;           // TODO: document

  // Validate our arguments.
  Index = Option.find("-");
  if (Index == std::string::npos)
    { return (ERROR_INVALID_ARGUMENT); }

  // Decompose the short as needed into all of its short options.
  for (Index = 1; Index < Option.length(); Index++)
  {
    Parameter.clear();                  // Clear the object.
    KeyText = Option.substr(Index, 1);  // Get the key name.
    Key = CKey::toKey(KeyText);         // Convert the text to the key.
    Parameter.setKey(Key);              // Set the key name.
    m_ParameterList.append(Parameter);  // Add the key-value to our list.
  }

  // Finally, return success.
  return (ERROR_NONE);
}


} // namespace code_sheriff


// TODO: configuration default is sheriff.conf
// also, a configuration file can be supplied via the command line as follows"
// /shrug?   --conf=MyFile
